/*-------------------------------------------------------------------------
 *
 * tid.c
 *	  内置类型元组 id 的函数
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/tid.c
 *
 * NOTES
 *	  输入例程大部分来源于 boxin()。
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <math.h>
#include <limits.h>

#include "access/heapam.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "common/hashfn.h"
#include "libpq/pqformat.h"
#include "miscadmin.h"
#include "parser/parsetree.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/snapmgr.h"
#include "utils/varlena.h"


#define DatumGetItemPointer(X)	 ((ItemPointer) DatumGetPointer(X))
#define ItemPointerGetDatum(X)	 PointerGetDatum(X)
#define PG_GETARG_ITEMPOINTER(n) DatumGetItemPointer(PG_GETARG_DATUM(n))
#define PG_RETURN_ITEMPOINTER(x) return ItemPointerGetDatum(x)

#define LDELIM			'('
#define RDELIM			')'
#define DELIM			','
#define NTIDARGS		2

static ItemPointer fc_currtid_for_view(Relation fc_viewrel, ItemPointer fc_tid);

/* ----------------------------------------------------------------
 *		tidin
 * ----------------------------------------------------------------
 */
Datum tidin(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	char	   *fc_p,
			   *fc_coord[NTIDARGS];
	int			fc_i;
	ItemPointer fc_result;
	BlockNumber fc_blockNumber;
	OffsetNumber fc_offsetNumber;
	char	   *fc_badp;
	unsigned long fc_cvt;

	for (fc_i = 0, fc_p = fc_str; *fc_p && fc_i < NTIDARGS && *fc_p != RDELIM; fc_p++)
		if (*fc_p == DELIM || (*fc_p == LDELIM && fc_i == 0))
			fc_coord[fc_i++] = fc_p + 1;

	if (fc_i < NTIDARGS)
#ifdef FDD//cppcheck
	{
#endif
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"tid", fc_str)));
#ifdef FDD//cppcheck
		PG_RETURN_VOID();
	}
#endif
	errno = 0;
	fc_cvt = strtoul(fc_coord[0], &fc_badp, 10);
	if (errno || *fc_badp != DELIM)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"tid", fc_str)));
	fc_blockNumber = (BlockNumber) fc_cvt;

	/*
	 * 处理无符号长整型可能比 BlockNumber 宽的情况，在这种情况下，strtoul 对一些超出 BlockNumber 范围的值不抛出错误。 （参见 oidin() 中的类似代码。）
	 */
#if SIZEOF_LONG > 4
	if (fc_cvt != (unsigned long) fc_blockNumber &&
		fc_cvt != (unsigned long) ((int32) fc_blockNumber))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"tid", fc_str)));
#endif

	fc_cvt = strtoul(fc_coord[1], &fc_badp, 10);
	if (errno || *fc_badp != RDELIM ||
		fc_cvt > USHRT_MAX)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
				 errmsg("invalid input syntax for type %s: \"%s\"",
						"tid", fc_str)));
	fc_offsetNumber = (OffsetNumber) fc_cvt;

	fc_result = (ItemPointer) palloc(sizeof(ItemPointerData));

	ItemPointerSet(fc_result, fc_blockNumber, fc_offsetNumber);

	PG_RETURN_ITEMPOINTER(fc_result);
}

/* ----------------------------------------------------------------
 *		tidout
 * ----------------------------------------------------------------
 */
Datum tidout(PG_FUNCTION_ARGS)
{
	ItemPointer fc_itemPtr = PG_GETARG_ITEMPOINTER(0);
	BlockNumber fc_blockNumber;
	OffsetNumber fc_offsetNumber;
	char		fc_buf[32];

	fc_blockNumber = ItemPointerGetBlockNumberNoCheck(fc_itemPtr);
	fc_offsetNumber = ItemPointerGetOffsetNumberNoCheck(fc_itemPtr);

	/* 也许有一天我们应该将其作为记录输出。 */
	snprintf(fc_buf, sizeof(fc_buf), "(%u,%u)", fc_blockNumber, fc_offsetNumber);

	PG_RETURN_CSTRING(pstrdup(fc_buf));
}

/*
 *		tidrecv			- 将外部二进制格式转换为 tid
 */
Datum tidrecv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	ItemPointer fc_result;
	BlockNumber fc_blockNumber;
	OffsetNumber fc_offsetNumber;

	fc_blockNumber = pq_getmsgint(fc_buf, sizeof(fc_blockNumber));
	fc_offsetNumber = pq_getmsgint(fc_buf, sizeof(fc_offsetNumber));

	fc_result = (ItemPointer) palloc(sizeof(ItemPointerData));

	ItemPointerSet(fc_result, fc_blockNumber, fc_offsetNumber);

	PG_RETURN_ITEMPOINTER(fc_result);
}

/*
 *		tidsend			- 将 tid 转换为二进制格式
 */
Datum tidsend(PG_FUNCTION_ARGS)
{
	ItemPointer fc_itemPtr = PG_GETARG_ITEMPOINTER(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendint32(&fc_buf, ItemPointerGetBlockNumberNoCheck(fc_itemPtr));
	pq_sendint16(&fc_buf, ItemPointerGetOffsetNumberNoCheck(fc_itemPtr));
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/*****************************************************************************
 *	 公共例程														 *
 *****************************************************************************/

Datum tideq(PG_FUNCTION_ARGS)
{
	ItemPointer fc_arg1 = PG_GETARG_ITEMPOINTER(0);
	ItemPointer fc_arg2 = PG_GETARG_ITEMPOINTER(1);

	PG_RETURN_BOOL(ItemPointerCompare(fc_arg1, fc_arg2) == 0);
}

Datum tidne(PG_FUNCTION_ARGS)
{
	ItemPointer fc_arg1 = PG_GETARG_ITEMPOINTER(0);
	ItemPointer fc_arg2 = PG_GETARG_ITEMPOINTER(1);

	PG_RETURN_BOOL(ItemPointerCompare(fc_arg1, fc_arg2) != 0);
}

Datum tidlt(PG_FUNCTION_ARGS)
{
	ItemPointer fc_arg1 = PG_GETARG_ITEMPOINTER(0);
	ItemPointer fc_arg2 = PG_GETARG_ITEMPOINTER(1);

	PG_RETURN_BOOL(ItemPointerCompare(fc_arg1, fc_arg2) < 0);
}

Datum tidle(PG_FUNCTION_ARGS)
{
	ItemPointer fc_arg1 = PG_GETARG_ITEMPOINTER(0);
	ItemPointer fc_arg2 = PG_GETARG_ITEMPOINTER(1);

	PG_RETURN_BOOL(ItemPointerCompare(fc_arg1, fc_arg2) <= 0);
}

Datum tidgt(PG_FUNCTION_ARGS)
{
	ItemPointer fc_arg1 = PG_GETARG_ITEMPOINTER(0);
	ItemPointer fc_arg2 = PG_GETARG_ITEMPOINTER(1);

	PG_RETURN_BOOL(ItemPointerCompare(fc_arg1, fc_arg2) > 0);
}

Datum tidge(PG_FUNCTION_ARGS)
{
	ItemPointer fc_arg1 = PG_GETARG_ITEMPOINTER(0);
	ItemPointer fc_arg2 = PG_GETARG_ITEMPOINTER(1);

	PG_RETURN_BOOL(ItemPointerCompare(fc_arg1, fc_arg2) >= 0);
}

Datum bttidcmp(PG_FUNCTION_ARGS)
{
	ItemPointer fc_arg1 = PG_GETARG_ITEMPOINTER(0);
	ItemPointer fc_arg2 = PG_GETARG_ITEMPOINTER(1);

	PG_RETURN_INT32(ItemPointerCompare(fc_arg1, fc_arg2));
}

Datum tidlarger(PG_FUNCTION_ARGS)
{
	ItemPointer fc_arg1 = PG_GETARG_ITEMPOINTER(0);
	ItemPointer fc_arg2 = PG_GETARG_ITEMPOINTER(1);

	PG_RETURN_ITEMPOINTER(ItemPointerCompare(fc_arg1, fc_arg2) >= 0 ? fc_arg1 : fc_arg2);
}

Datum tidsmaller(PG_FUNCTION_ARGS)
{
	ItemPointer fc_arg1 = PG_GETARG_ITEMPOINTER(0);
	ItemPointer fc_arg2 = PG_GETARG_ITEMPOINTER(1);

	PG_RETURN_ITEMPOINTER(ItemPointerCompare(fc_arg1, fc_arg2) <= 0 ? fc_arg1 : fc_arg2);
}

Datum hashtid(PG_FUNCTION_ARGS)
{
	ItemPointer fc_key = PG_GETARG_ITEMPOINTER(0);

	/*
	 * 尽管您可能会遇到一个强制要求将填充空间附加到结构 ItemPointerData 的编译器，但我们至少可以通过不使用 sizeof(ItemPointerData) 使此代码工作。
	 * 相反，依靠知道组件字段的大小。
	 */
	return hash_any((unsigned char *) fc_key,
					sizeof(BlockIdData) + sizeof(OffsetNumber));
}

Datum hashtidextended(PG_FUNCTION_ARGS)
{
	ItemPointer fc_key = PG_GETARG_ITEMPOINTER(0);
	uint64		fc_seed = PG_GETARG_INT64(1);

	/* As above */
	return hash_any_extended((unsigned char *) fc_key,
							 sizeof(BlockIdData) + sizeof(OffsetNumber),
							 fc_seed);
}


/*
 *	用于获取指定元组的最新 tid 的函数。
 *
 *	也许这些实现应该移到其他地方
 */

/*
 * 当前 CTID 函数的实用包装器。
 *		返回指向“tid”的元组的最新版本，针对关系“rel”。
 */
static ItemPointer fc_currtid_internal(Relation fc_rel, ItemPointer fc_tid)
{
	ItemPointer fc_result;
	AclResult	fc_aclresult;
	Snapshot	fc_snapshot;
	TableScanDesc fc_scan;

	fc_result = (ItemPointer) palloc(sizeof(ItemPointerData));

	fc_aclresult = pg_class_aclcheck(RelationGetRelid(fc_rel), GetUserId(),
								  ACL_SELECT);
	if (fc_aclresult != ACLCHECK_OK)
		aclcheck_error(fc_aclresult, get_relkind_objtype(fc_rel->rd_rel->relkind),
					   RelationGetRelationName(fc_rel));

	if (fc_rel->rd_rel->relkind == RELKIND_VIEW)
		return fc_currtid_for_view(fc_rel, fc_tid);

	if (!RELKIND_HAS_STORAGE(fc_rel->rd_rel->relkind))
		elog(ERROR, "cannot look at latest visible tid for relation \"%s.%s\"",
			 get_namespace_name(RelationGetNamespace(fc_rel)),
			 RelationGetRelationName(fc_rel));

	ItemPointerCopy(fc_tid, fc_result);

	fc_snapshot = RegisterSnapshot(GetLatestSnapshot());
	fc_scan = table_beginscan_tid(fc_rel, fc_snapshot);
	table_tuple_get_latest_tid(fc_scan, fc_result);
	table_endscan(fc_scan);
	UnregisterSnapshot(fc_snapshot);

	return fc_result;
}

/*
 *	处理视图的 CTID。
 *		CTID 应在视图中定义，并且必须对应于基关系的 CTID。
 */
static ItemPointer fc_currtid_for_view(Relation fc_viewrel, ItemPointer fc_tid)
{
	TupleDesc	fc_att = RelationGetDescr(fc_viewrel);
	RuleLock   *fc_rulelock;
	RewriteRule *fc_rewrite;
	int			fc_i,
				fc_natts = fc_att->natts,
				fc_tididx = -1;

	for (fc_i = 0; fc_i < fc_natts; fc_i++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_att, fc_i);

		if (strcmp(NameStr(fc_attr->attname), "ctid") == 0)
		{
			if (fc_attr->atttypid != TIDOID)
				elog(ERROR, "ctid isn't of type TID");
			fc_tididx = fc_i;
			break;
		}
	}
	if (fc_tididx < 0)
		elog(ERROR, "currtid cannot handle views with no CTID");
	fc_rulelock = fc_viewrel->rd_rules;
	if (!fc_rulelock)
		elog(ERROR, "the view has no rules");
	for (fc_i = 0; fc_i < fc_rulelock->numLocks; fc_i++)
	{
		fc_rewrite = fc_rulelock->rules[fc_i];
		if (fc_rewrite->event == CMD_SELECT)
		{
			Query	   *fc_query;
			TargetEntry *fc_tle;

			if (list_length(fc_rewrite->actions) != 1)
				elog(ERROR, "only one select rule is allowed in views");
			fc_query = (Query *) linitial(fc_rewrite->actions);
			fc_tle = get_tle_by_resno(fc_query->targetList, fc_tididx + 1);
			if (fc_tle && fc_tle->expr && IsA(fc_tle->expr, Var))
			{
				Var		   *fc_var = (Var *) fc_tle->expr;
				RangeTblEntry *fc_rte;

				if (!IS_SPECIAL_VARNO(fc_var->varno) &&
					fc_var->varattno == SelfItemPointerAttributeNumber)
				{
					fc_rte = rt_fetch(fc_var->varno, fc_query->rtable);
					if (fc_rte)
					{
						ItemPointer fc_result;
						Relation	fc_rel;

						fc_rel = table_open(fc_rte->relid, AccessShareLock);
						fc_result = fc_currtid_internal(fc_rel, fc_tid);
						table_close(fc_rel, AccessShareLock);
						return fc_result;
					}
				}
			}
			break;
		}
	}
	elog(ERROR, "currtid cannot handle this view");
	return NULL;
}

/*
 * currtid_byrelname
 *		获取指向 CTID 的元组的最新版本，针对给定的关系名称。
 */
Datum currtid_byrelname(PG_FUNCTION_ARGS)
{
	text	   *fc_relname = PG_GETARG_TEXT_PP(0);
	ItemPointer fc_tid = PG_GETARG_ITEMPOINTER(1);
	ItemPointer fc_result;
	RangeVar   *fc_relrv;
	Relation	fc_rel;

	fc_relrv = makeRangeVarFromNameList(textToQualifiedNameList(fc_relname));
	fc_rel = table_openrv(fc_relrv, AccessShareLock);

	/* 获取与此 CTID 关联的最新元组版本 */
	fc_result = fc_currtid_internal(fc_rel, fc_tid);

	table_close(fc_rel, AccessShareLock);

	PG_RETURN_ITEMPOINTER(fc_result);
}
